-- Copyright (c) 2015,2016 Lucky Byte, Inc.
--
-- Summary : 代付实时追付
--
-- Author  : 吴小虎 <xiaohu@lucky-byte.com>
-- Since   : 2016.5.20
--
-- History
--  * 2016.5.20 : 创建此文件
--  * 2016.7.12 : 重构以适应新版
--  * 2016.7.13 : 清理过期未追付的记录
--  * 2016.7.20 : 支持助农取款追付
--
require "core.lib"

-- 删除隔日还未追付的记录，可能由于定时任务关闭或追付时间过期导致
jdbc.update([[
    delete from pay_t40_recvr as r where r.done = false and
        r.ctime::date < CURRENT_DATE
]])

-- 解析命令行参数
local _optargs, _optind = core.getopt(arg, "e:", {
    endtime = "e",    -- 当日截止代付时间
})
_optargs.endtime = _optargs.e
if not _optargs.endtime then
    logger.debug('没有指定当日截止代付时间，系统将使用默认值[22:30:00]，' ..
        '可以通过参数[--endtime=HH:mm:ss]设置当日截止代付时间.')
    _optargs.endtime = '22:30:00'
end
logger.info('追付交易截止时间：%s', _optargs.endtime)

-- 名字空间
local _meta = {}

-- 查询一条已复核未发送的追付记录, 仅当日交易可追付
local _table = jdbc.select([[
    select r.*,
        case when t.chnl in (1,4) then
            json_extract_path_text(t.i_pkt, '42')
        end as merno,
        case when t.chnl in (1,4) then
            json_extract_path_text(t.i_pkt, '41')
        end as termid,
        case when t.chnl in (1,4) then
            json_extract_path_text(t.i_pkt, '4')
        end as tramt
    from pay_t40_recvr as r
    left join pay_trlist as t on t.uuid = r.tr_uuid_1 and t.incoming = true
    where r.checked = true and r.done = false and
        r.ctime::date = CURRENT_DATE and r.ctime::time < ?
    limit 1
]], _optargs.endtime)
if not _table then
    logger.error('查询待追付记录失败，请检查参数是否正确...')
    return
end
if _table.count == 0 then
    logger.trace('没有查询到待追付记录，处理完毕...')
    return
end
_meta.recvr_record = _table.get_record(0)

-- 原交易金额
local _tramt = _meta.recvr_record.get('tramt')
if not _tramt then
    logger.error('原交易金额为空，追付失败.')
    return
end
local _tramt = tonumber(_tramt) / 100

-- 经过用户复核的代付金额
_meta.t40_amt = _meta.recvr_record.get('t40_amt')
if _meta.t40_amt > _tramt then
    logger.error('代付金额[%s]大于原交易金额[%s]，不允许追付.',
        _meta.t40_amt, _tramt)
    return
end

-- 查询代付商户信息
local _table = jdbc.select([[
    select * from pay_merch where uuid = ?
]], _meta.recvr_record.get('pay_merid'))
if _table.count == 0 then
    logger.error('未查询到代付商户[%s]的信息，追付失败.',
        _meta.recvr_record.get('pay_merid') or '')
    return
end
_meta.pay_merch = _table.get_record(0)

-- 追付的产品
local _product = _meta.recvr_record.get('product')

-- 如果是助农取款追付，查询助农取款资金池信息
if _product == 'p2' then
    local _table = jdbc.select([[
        select * from pay_p2_fund where uuid = ?
    ]], _meta.recvr_record.get('p2_fund'))
    if _table.count == 0 then
        logger.error('未查询到助农取款资金池[%s]的信息，追付失败.',
            _meta.recvr_record.get('p2_fund') or '')
        return
    end
    _meta.p2_fund = _table.get_record(0)
end

-- 首先更新已代付标志，避免重复代付
--
local _table = jdbc.select([[
    update pay_t40_recvr as r1 set done = true from (
        select done from pay_t40_recvr where uuid = ? for update
    ) as r2
    where uuid = ?
    returning r2.done as old_done, r1.done as new_done
]], _meta.recvr_record.get('uuid'), _meta.recvr_record.get('uuid'))
if not _table then
    logger.error('更新追付完成标志失败，放弃处理...')
    return
end
local _record = _table.get_record(0)
if _record.get('old_done') then
    logger.error('该追付记录已被其它线程或进程追付，不能重复追付...')
    return
end
if not _record.get('new_done') then
    logger.error('更新追付完成标志失败，放弃处理...')
    return
end
logger.info('更新追付完成标志成功，开始发送代付交易...')

-- 追付交易流水 UUID
_meta.pay_uuid = uuid()

-- 通过银联全渠道进行代付
local function _pay_by_chnl_3()
    local _071_packet = lib071.new_packet()

    _071_packet.set('txnType', '12')
    _071_packet.set('txnSubType', '00')
    _071_packet.set('bizType', '000401')
    _071_packet.set('txnTime', os.date('%Y%m%d%H%M%S'))
    _071_packet.set('accessType', '0')
    _071_packet.set('txnAmt', round(_meta.t40_amt * 100, 0))
    _071_packet.set('merId', _meta.pay_merch.get('merno'))
    _071_packet.set('orderId', core.order_id())
    _071_packet.set('accType', '01')
    _071_packet.set('accNo',  _meta.recvr_record.get('acct_no'))
    _071_packet.set('termId', _meta.recvr_record.get('termid'))

    -- 构建身份验证信息字符串
    local _custinfo = core.acp_aux.build_custinfo({
        acct_name = _meta.recvr_record.get('acct_name'),
        cred_no   = _meta.recvr_record.get('acct_cred_no'),
        cred_type = _meta.recvr_record.get('acct_cred_type')
    })
    _071_packet.set('customerInfo', _custinfo)

    -- 发送报文，返回结果
    local _fowd = lib071.forward({
        uuid = _meta.pay_uuid, packet = _071_packet, indirect = true,
        merch = _meta.pay_merch
    })
    if not _fowd.respkt or not lib071.respkt_ok(_fowd.respkt) then
        logger.warn('全渠道代付交易发送失败...')
        return false
    end
    logger.trace('全渠道代付交易发送成功，需进一步等待后台通知确定此交易最终状态...')
    return true
end


local _do_pay = function()
    -- 通过指定的代付渠道进行代付
    local _pay_chnl = _meta.recvr_record.get('pay_chnl')
    local _pay_ret
    if tonumber(_pay_chnl) == 3 then    -- 全渠道
        _pay_ret = _pay_by_chnl_3(_pay_uuid)
    else
        logger.error('代付渠道[%s]无效或不支持，请检查.', _pay_chnl or '')
        return false
    end
    -- 更新追付记录信息
    local _status = _pay_ret and 2 or 3
    local _notes  = _pay_ret and '代付交易成功，待确认' or '代付交易失败，待确认'
    jdbc.update([[
        update pay_t40_recvr set done_uuid = ?,
            done_time = CURRENT_TIMESTAMP, status = ?, notes = ?
        where uuid = ?
    ]], _meta.pay_uuid, _status, _notes, _meta.recvr_record.get('uuid'))
    return _pay_ret
end


-- 消费代付追付
--
local _recvr_p1 = function()
    if not _do_pay() then
        logger.info('代付交易响应报文指示此交易失败.')
    end
    -- 成功后需要更新代付订单的追付标志、代付流水等信息
    local _order_uuid = _meta.recvr_record.get('order_uuid')
    logger.debug('更新消费代付订单[%s]的代付流水为此追付交易流水.', _order_uuid)
    jdbc.update([[
        update pay_p1_order set recover = true, status = 5,
            recvr_uuid = ?, t40_uuid_old = t40_uuid, t40_uuid = ?,
            notes = '已追付，待确认'
        where uuid = ?
    ]], _meta.recvr_record.get('uuid'), _meta.pay_uuid, _order_uuid)
end


-- 从资金池扣除手续费
--
local _take_fee_from_fund = function(p2_fee)
    -- 查询和修改资金池，在一个事务中处理
    jdbc.begin()

    local _table = jdbc.select([[
        select * from pay_p2_fund where uuid = ? for update
    ]], _meta.recvr_record.get('p2_fund'))

    if _table.count == 0 then
        notify.error('追付记录[%s]中资金池[%s]无效.',
            _meta.recvr_record.get('uuid'),
            _meta.recvr_record.get('p2_fund') or '')
        return false
    end
    local _p2_fund = _table.get_record(0)

    -- 如果资金池余额足以支付手续费，则直接扣除手续费即可
    local _remains = _p2_fund.get('fund_remains')
    if _remains >= p2_fee then
        logger.debug('资金池余额[%s] >= 手续费[%s]，将从资金池余额扣取手续费...',
            _remains, p2_fee)

        if not core.p2_fund.take_from({
            p2_fund_uuid = _p2_fund.get('uuid'),
            amt = p2_fee,
            merno  = _meta.recvr_record.get('merno'),
            termid = _meta.recvr_record.get('termid')
        }) then
            jdbc.rollback()
            logger.error('从资金池扣取手续费失败，代付失败.')
            return false
        end
        jdbc.commit()   -- 结束数据库处理，提交事务
        return true, _meta.t40_amt, 1
    end

    -- 如果资金池余额不足，则根据配置进行不同的处理
    local _fund_lack_proc = _meta.recvr_record.get('fund_lack_proc')

    -- 转从代付金额中扣取手续费
    if _fund_lack_proc == 4 then
        jdbc.rollback()
        logger.info(
            '资金池余额[%s]不足以支付手续费[%s]，根据配置，转从代付金额中扣取手续费.',
            _remains, p2_fee)
        return true, _meta.t40_amt - p2_fee, 2
    end

    -- 透支资金池，超额度时不允许代付交易
    if _fund_lack_proc == 3 then
        logger.info(
            '资金池余额[%s]不足以支付手续费[%s]，根据配置，将透支资金池支付手续费.',
            _remains, p2_fee)

        local _overdraw = _meta.recvr_record.get('fund_overdraw')
        if not _overdraw then
            jdbc.rollback()
            notify.warn('追付记录[%s]错误[未设置资金池透支额度].',
                _meta.recvr_record.get('uuid'))
            return false
        end
        if _overdraw < 0.0 then
            jdbc.rollback()
            notify.warn('追付记录[%s]错误[资金池透支额度[%s]为负数].',
                _meta.recvr_record.get('uuid'), _overdraw)
            return false
        end
        -- 如果资金池余额加上透支额度足以支付手续费，则从资金池余额中扣取手续费
        -- 这将导致资金池余额小于 0
        if _remains + _overdraw < p2_fee then
            jdbc.rollback()
            notify.error('资金池余额[%s] + 透支额度[%s] < 手续费[%s]，根据配置，不允许继续代付.',
                _remains, _overdraw, p2_fee)
            return false
        end
        logger.debug('资金池余额[%s] + 透支额度[%s] >= 手续费[%s]，从资金池余额扣取手续费...',
            _remains, _overdraw, p2_fee)

        if not core.p2_fund.take_from({
            p2_fund_uuid = _p2_fund.get('uuid'),
            amt = p2_fee,
            merno  = _meta.recvr_record.get('merno'),
            termid = _meta.recvr_record.get('termid')
        }) then
            jdbc.rollback()
            logger.error('从资金池扣取手续费失败，代付失败.')
            return false
        end
        jdbc.commit()   -- 结束数据库处理，提交事务
        return true, _meta.t40_amt, 1
    end

    -- 不允许助农取款交易 或 允许助农取款交易，但不允许代付交易
    jdbc.rollback()
    notify.error('资金池[%s][%s]余额[%s]不足以支付手续费[%s]，根据配置，不允许继续代付.',
        _p2_fund.get('name'), _p2_fund.get('uuid'), _remains, p2_fee)
    return false
end


-- 助农取款代付追付
--
local _recvr_p2 = function()
    local _order_uuid = _meta.recvr_record.get('order_uuid')
    local _table = jdbc.select([[
        select p2_fee from pay_p2_order where uuid = ?
    ]], _order_uuid)
    if not _table or _table.count == 0 then
        logger.error('查询待追付记录[%s]中助农取款代付订单[%s]信息失败.',
            _meta.recvr_record.get('uuid'), _order_uuid)
        return
    end
    local _order_record = _table.get_record(0)

    local _p2_fee = _order_record.get('p2_fee')
    local _fee_take_from = nil
    local _pay_amt = -1

    -- 根据手续费收取方式配置，计算代付金额
    --  1: 从资金池扣取
    --  2: 从代付金额扣取
    local _fee_take_type = _meta.recvr_record.get('fee_take_type')
    if _fee_take_type == 1 then
        logger.trace('根据配置，将从资金池扣取手续费...')
        _succed, _pay_amt, _fee_take_from = _take_fee_from_fund(_p2_fee)
        if not _succed then
            logger.error('扣取手续费失败，停止代付.')
            return
        end
    elseif _fee_take_type == 2 then
        logger.trace('根据配置，将从代付金额中扣取手续费...')
        _pay_amt = _meta.t40_amt - _p2_fee
        _fee_take_from = 2
    else
        notify.error('助农取款代付[%s]配置错误[手续费收取方式[%s]无效]',
            tz1_meta.p2_record.get('uuid'), _fee_take_type)
        return
    end

    -- 更新订单手续费收取方式，后续可能需要归还
    jdbc.update([[
        update pay_p2_order set fee_take_from = ?, p2_fund_uuid = ?
            where uuid = ?
    ]], _fee_take_from, _meta.recvr_record.get('p2_fund'), _order_uuid)

    _meta.t40_amt = _pay_amt
    if _meta.t40_amt > _tramt then
        logger.error('实际代付金额[%s]不能大于原交易金额[%s]，代付失败.',
            _meta.t40_amt, _tramt)
        if _fee_take_from == 1 then
            core.p2_fund.give_back({
                p2_fund_uuid = _meta.recvr_record.get('p2_fund'),
                amt = _p2_fee,
                merno  = _meta.recvr_record.get('merno'),
                termid = _meta.recvr_record.get('termid')
            })
        end
        return
    end
    if _meta.t40_amt <= 0.0 then
        logger.error('实际代付金额[%s]不能小于或等于 0，代付失败.', _meta.t40_amt)
        if _fee_take_from == 1 then
            core.p2_fund.give_back({
                p2_fund_uuid = _meta.recvr_record.get('p2_fund'),
                amt = _p2_fee,
                merno  = _meta.recvr_record.get('merno'),
                termid = _meta.recvr_record.get('termid')
            })
        end
        return
    end
    if not _do_pay() then
        logger.trace('代付交易响应报文指示交易失败，待确认后归还手续费...')
        --[[
        if _fee_take_from == 1 then
            core.p2_fund.give_back({
                p2_fund_uuid = _meta.recvr_record.get('p2_fund'),
                amt = _p2_fee,
                merno  = _meta.recvr_record.get('merno'),
                termid = _meta.recvr_record.get('termid')
            })
        end
        return
        ]]
    end
    logger.debug('更改助农代付订单[%s]的代付流水为此追付交易流水.', _order_uuid)
    jdbc.update([[
        update pay_p2_order set recover = true, status = 5,
            recvr_uuid = ?, t40_uuid_old = t40_uuid, t40_uuid = ?,
            notes = '已追付，待确认'
        where uuid = ?
    ]], _meta.recvr_record.get('uuid'), _meta.pay_uuid, _order_uuid)
end


if _product == 'p1' then
    return _recvr_p1()
end
if _product == 'p2' then
    return _recvr_p2()
end
logger.error('追付产品[%s]无效.', _product)
